#include "depui/depui.h"

#define MXMODULE_SCROLL

static void mx__listelem_unlink(MX_LISTELEM_DATA * listelem)
{
	MXDEBUG_INVARIANT(MXOBJ(listelem));

	if (mx_dllist_list(listelem))
		MX_DLLIST_REMOVE(listelem);
}

static void mx__listelem_scroll(MX_LISTELEM_DATA * listelem)
{
	MX_LIST_DATA *list = mx_dllist_list(listelem);
	MX_LISTELEM_DATA *first = mx_dllist_first(list);

	if ((list) && (first)) {
		MX_RECT rrect;

		rrect.x1 = mx_x1(listelem) - mx_x1(first);
		rrect.y1 = mx_y1(listelem) - mx_y1(first);
		rrect.x2 = mx_x1(listelem) - mx_x1(first) - 1;
		rrect.y2 = mx_y2(listelem) - mx_y1(first) - 1;

		mx_emit(MX_SCROLLAREA, &rrect, sizeof(rrect));
	}
}

void mx_listelem_class(void)
{
	MX_LISTELEM *l = (MX_LISTELEM *) mx.obj;
	MX_LISTELEM_DATA *listelem = MXLISTELEM(l);

	MXDEBUG_CLASSINVARIANT(MXOBJ(l));

	switch (mx.event) {

	case MX_DESTRUCT:
		mx__listelem_unlink(listelem);
		break;

	case MX_POINTER_PRESS:
		mx_button_class();
		mx__listelem_scroll(listelem);
		break;

	case MX_EXPOSE:
		mx__theme->listelem(l);
		return;

	case MX_SELECT:
		{
			MX_LIST_DATA *list = mx_dllist_list(listelem);

			if ((list) && (!list->_changeing)) {
				if (!list->_multiple) {
					MX_LISTELEM_DATA *ptr = mx_dllist_first(list);
					unsigned waschanging = list->_changeing;

					list->_changeing = true;

					while (ptr) {
						unsigned int wasselected = mx_selected(ptr);

						assert(mx_dllist_list(ptr) == list);

						if ((ptr != listelem) && (wasselected)) {
							mx_select(ptr, false);
							mx_dirty(ptr, true);
						}

						ptr = mx_dllist_next(ptr);
					}
					list->_changeing = waschanging;
				}

				mx_button_class();

				mx_event(list, MX_LIST_CHANGED, 0, 0);
				return;
			}
		}
		break;

	case MX_DEFAULTRECT:
		mx_textual_class();
		return;

	default:
		break;
	}

	mx_button_class();
}

MX_LISTELEM *mx__listelem(MX_LISTELEM_DATA * listelem, size_t size, MX_LIST_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_LISTELEM);

	listelem = (MX_LISTELEM_DATA *) mx_button(listelem, size, parent, theid);
	if (listelem) {
		MXOBJ(listelem)->_class = mx_listelem_class;
		MXDEBUG_ATOMNAME(listelem, "listelem");
		MXDEBUG_ALLOCTAG(listelem);

		mx_text_align(listelem, MX_ALIGN_LEFT);

		if (parent)
			MX_DLLIST_APPEND(listelem, parent);
	}
	MXDEBUG_INVARIANT(MXOBJ(listelem));
	return (MX_LISTELEM *) listelem;
}

static void mx__list_size(MX_LIST_DATA * list, int *lw, int *lh)
{
	MX_LISTELEM_DATA *ptr = mx_dllist_first(list);
	int y = 0, w = 0;

	while (ptr) {
		mx_defaultrect(ptr, 0);
		mx_move(ptr, 0, y);

		y = mx_y(ptr) + mx_h(ptr) + 1;
		w = MX_MAX(w, mx_w(ptr));

		ptr = mx_dllist_next(ptr);
	}

	assert(lw);
	assert(lh);
	*lw = w;
	*lh = y - 1;
}

void mx_list_class(void)
{
	MX_LIST *l = (MX_LIST *) mx.obj;
	MX_LIST_DATA *list = MXLIST(l);

	MXDEBUG_CLASSINVARIANT(MXOBJ(l));

	switch (mx.event) {

	case MX_DESTRUCT:
		while (mx_dllist_first(list))
			mx__listelem_unlink(mx_dllist_first(list));
		break;

	case MX_DEFAULTRECT:
		{
			int y = 0, w = 0;
			MX_RECT *rrect = mx_defaultrect_data();

			mx__list_size(list, &w, &y);

			rrect->x2 = rrect->x1 + w;
			rrect->y2 = rrect->y1 + y;
			return;
		}

	case MX_GEOMETRY:
		{
			int y = 0, w = 0;
			MX_LISTELEM_DATA *ptr;

			mx__list_size(list, &w, &y);
			mx__scroll_geometry(MXSCROLL(list), 0, 0, w, y);

			ptr = mx_dllist_first(list);
			w = MX_MAX(w, MXSCROLL(list)->_vw);

			while (ptr) {
				mx_resize(ptr, w, MXDEFAULT);
				mx_geometry(ptr);
				ptr = mx_dllist_next(ptr);
			}
			return;
		}

	default:
		break;
	}
	mx_scroll_class();
}

MX_LIST *mx__list(MX_LIST_DATA * list, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_LIST);

	list = (MX_LIST_DATA *) mx__scroll(MXSCROLL(list), size, parent, theid);
	if (list) {
		MXOBJ(list)->_class = mx_list_class;
		MXDEBUG_ATOMNAME(list, "list");
		MXDEBUG_ALLOCTAG(list);
	}

	MXDEBUG_INVARIANT(MXOBJ(list));
	return (MX_LIST *) list;
}

void mx__list_multiple(MX_LIST_DATA * list, const unsigned multi)
{
	MXDEBUG_INVARIANT(MXOBJ(list));

	list->_multiple = multi;
}

MX_LISTELEM *mx__list_append(MX_LIST_DATA * list, const char *text, int len, MX_FREE dfree, int theid)
{
	MX_LISTELEM *listelem = mx__listelem(0, 0, list, theid);

	MXDEBUG_INVARIANT(MXOBJ(list));

	mx_text_set(listelem, text, len, dfree);

	return listelem;
}

void mx__list_select_id(MX_LIST_DATA * list, int theid, unsigned doit)
{
	MX_LISTELEM_DATA *ptr = mx_dllist_first(list);

	while (ptr) {
		if (MXID(ptr) == theid) {
			if ((mx_selected(ptr) && (!doit))
				|| (!mx_selected(ptr) && (doit)))
				mx_select(ptr, doit);
		}
		ptr = mx_dllist_next(ptr);
	}
}

void mx__list_empty(MX_LIST_DATA * list)
{
	MXDEBUG_INVARIANT(MXOBJ(list));

	while (mx_dllist_first(list)) {
		MX_LISTELEM_DATA *listelem = mx_dllist_first(list);

		mx__listelem_unlink(listelem);

		MX_TREE_REMOVE(MXOBJ(listelem));
		mx_delete(listelem);
	}
}

MX_LISTELEM *mx__list_first(MX_LIST_DATA * list)
{
	MXDEBUG_INVARIANT(MXOBJ(list));

	return (MX_LISTELEM *) mx_dllist_first(list);
}

MX_LISTELEM *mx__list_last(MX_LIST_DATA * list)
{
	MXDEBUG_INVARIANT(MXOBJ(list));

	return (MX_LISTELEM *) mx_dllist_last(list);
}

MX_LISTELEM *mx__list_next(MX_LISTELEM_DATA * listelem)
{
	MXDEBUG_INVARIANT(MXOBJ(listelem));

	return (MX_LISTELEM *) mx_dllist_next(listelem);
}

MX_LISTELEM *mx__list_prev(MX_LISTELEM_DATA * listelem)
{
	MXDEBUG_INVARIANT(MXOBJ(listelem));

	return (MX_LISTELEM *) mx_dllist_prev(listelem);
}

MX_LISTELEM *mx__list_selected(MX_LIST_DATA * list, MX_LISTELEM_DATA * ptr)
{
	if (!ptr)
		ptr = mx_dllist_first(list);

	else
		ptr = mx_dllist_next(ptr);

	MXDEBUG_INVARIANT(MXOBJ(list));

	while ((ptr) && (!mx_selected(ptr)))
		ptr = mx_dllist_next(ptr);

	return (MX_LISTELEM *) ptr;
}

int mx__list_selected_id(MX_LIST_DATA * list)
{
	MX_LISTELEM *ptr = mx__list_selected(list, 0);

	if (ptr)
		return MXID(ptr);

	return -1;
}
